package com.iaiai.imail.helper.mail;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.Date;

import javax.mail.BodyPart;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.NoSuchProviderException;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;

import com.iaiai.imail.core.Attribute;
import com.iaiai.imail.core.IServiceFactory;

/**
 * 
 * <p>
 * Title: MailReceiver.java
 * </p>
 * <p>
 * E-Mail: 176291935@qq.com
 * </p>
 * <p>
 * QQ: 176291935
 * </p>
 * <p>
 * Http: iaiai.iteye.com
 * </p>
 * <p>
 * Create time: 2012-9-15 下午3:44:59
 * </p>
 * 
 * @author 丸子
 * @version 0.0.1
 */
public class MailReceiver {

	// 收邮件的参数配置
	private MailReceiverInfo receiverInfo;
	// 与邮件服务器连接后得到的邮箱
	private Store store;
	// 收件箱
	private Folder folder;
	// 收件箱中的邮件消息
	private Message[] messages;
	// 当前正在处理的邮件消息
	private Message currentMessage;

	private String currentEmailFileName;

	public MailReceiver(MailReceiverInfo receiverInfo) {
		this.receiverInfo = receiverInfo;
	}

	/**
	 * 收邮件
	 */
	public void receiveAllMail() throws Exception {
		if (this.receiverInfo == null) {
			throw new Exception("必须提供接收邮件的参数！");
		}
		// 连接到服务器
		if (this.connectToServer()) {
			// 打开收件箱
			if (this.openInBoxFolder()) {
				// 获取所有邮件
				this.getAllMail();
				this.closeConnection();
			} else {
				throw new Exception("打开收件箱失败！");
			}
		} else {
			throw new Exception("连接邮件服务器失败！");
		}
	}

	/**
	 * 登陆邮件服务器
	 */
	private boolean connectToServer() {
		// 判断是否需要身份认证
		MyAuthenticator authenticator = null;
		if (this.receiverInfo.isValidate()) {
			// 如果需要身份认证，则创建一个密码验证器
			authenticator = new MyAuthenticator(this.receiverInfo.getUserName(), this.receiverInfo.getPassword());
		}
		// 创建session
		Session session = Session.getInstance(this.receiverInfo.getProperties(), authenticator);

		// 创建store,建立连接
		try {
			this.store = session.getStore(this.receiverInfo.getProtocal());
		} catch (NoSuchProviderException e) {
			System.out.println("连接服务器失败！");
			return false;
		}

		System.out.println("connecting");
		try {
			this.store.connect();
		} catch (MessagingException e) {
			System.out.println("连接服务器失败！");
			return false;
		}
		System.out.println("连接服务器成功");
		return true;
	}

	/**
	 * 打开收件箱
	 */
	private boolean openInBoxFolder() {
		try {
			this.folder = store.getFolder("INBOX");
			// 只读
			folder.open(Folder.READ_ONLY);
			return true;
		} catch (MessagingException e) {
			System.err.println("打开收件箱失败！");
		}
		return false;
	}

	/**
	 * 断开与邮件服务器的连接
	 */
	private boolean closeConnection() {
		try {
			if (this.folder.isOpen()) {
				this.folder.close(true);
			}
			this.store.close();
			System.out.println("成功关闭与邮件服务器的连接！");
			return true;
		} catch (Exception e) {
			System.out.println("关闭和邮件服务器之间连接时出错！");
		}
		return false;
	}

	/**
	 * 获取messages中的所有邮件
	 * 
	 * @throws MessagingException
	 */
	private void getAllMail() throws MessagingException {
		// 从邮件文件夹获取邮件信息
		this.messages = this.folder.getMessages();
		System.out.println("总的邮件数目：" + messages.length);
		System.out.println("新邮件数目：" + this.getNewMessageCount());
		System.out.println("未读邮件数目：" + this.getUnreadMessageCount());
		// 将要下载的邮件的数量。
		int mailArrayLength = this.getMessageCount();
		System.out.println("一共有邮件" + mailArrayLength + "封");
		int errorCounter = 0; // 邮件下载出错计数器
		int successCounter = 0;
		for (int index = 0; index < mailArrayLength; index++) {
			//判断数据库中是否有此邮件，如果有此不用读取了
			if(IServiceFactory.getInstance().getInboxDao().isMessage(MailMessage.getMessageId(messages[index]))){
				continue;
			}
			try {
				this.currentMessage = (messages[index]); // 设置当前message
				System.out.println("正在获取第" + index + "封邮件");
				this.showMailBasicInfo();
				getMail(); // 获取当前message
				System.out.println("成功获取第" + index + "封邮件");
				successCounter++;
			} catch (Throwable e) {
				errorCounter++;
				System.err.println("下载第" + index + "封邮件时出错");
			}
		}
		System.out.println("------------------");
		System.out.println("成功下载了" + successCounter + "封邮件");
		System.out.println("失败下载了" + errorCounter + "封邮件");
		System.out.println("------------------");
	}

	/**
	 * 显示邮件的基本信息
	 */
	private void showMailBasicInfo() throws Exception {
		showMailBasicInfo(this.currentMessage);
	}

	private void showMailBasicInfo(Message message) throws Exception {
		System.out.println("-------- 邮件ID：" + MailMessage.getMessageId(message) + " ---------");
		System.out.println("From：" + MailMessage.getFrom(message));
		System.out.println("To：" + MailMessage.getTOAddress(message));
		System.out.println("CC：" + MailMessage.getCCAddress(message));
		System.out.println("BCC：" + MailMessage.getBCCAddress(message));
		System.out.println("Subject：" + MailMessage.getSubject(message));
		System.out.println("发送时间：：" + MailMessage.getSentDate(message));
		System.out.println("是新邮件？" + MailMessage.isNew(message));
		System.out.println("要求回执？" + MailMessage.getReplySign(message));
		System.out.println("包含附件？" + MailMessage.isContainAttach(message));
		System.out.println("------------------------------");
	}

	/**
	 * 获取messages中message的数量
	 * 
	 * @return
	 */
	private int getMessageCount() {
		return this.messages.length;
	}
	
	/**
	 * 获得所有邮件
	 * @return
	 */
	public Message[] getMessages(){
		return messages;
	}

	/**
	 * 获得收件箱中新邮件的数量
	 * 
	 * @return
	 * @throws MessagingException
	 */
	public int getNewMessageCount() throws MessagingException {
		return this.folder.getNewMessageCount();
	}

	/**
	 * 获得收件箱中未读邮件的数量
	 * 
	 * @return
	 * @throws MessagingException
	 */
	public int getUnreadMessageCount() throws MessagingException {
		return this.folder.getUnreadMessageCount();
	}

	/**
	 * 获得当前邮件
	 */
	private void getMail() throws Exception {
		try {
			this.saveMessageAsFile(currentMessage);
			this.parseMessage(currentMessage);
		} catch (IOException e) {
			throw new IOException("保存邮件出错，检查保存路径");
		} catch (MessagingException e) {
			throw new MessagingException("邮件转换出错");
		} catch (Exception e) {
			e.printStackTrace();
			throw new Exception("未知错误");
		}
	}

	/**
	 * 保存邮件源文件
	 */
	private void saveMessageAsFile(Message message) {
		try {
			// 将邮件的ID中尖括号中的部分做为邮件的文件名
			String oriFileName = getInfoBetweenBrackets(MailMessage.getMessageId(message).toString());
			// 设置文件后缀名。若是附件则设法取得其文件后缀名作为将要保存文件的后缀名，
			// 若是正文部分则用.htm做后缀名
			String emlName = oriFileName;
			String fileNameWidthExtension = this.receiverInfo.getEmailDir() + oriFileName
					+ this.receiverInfo.getEmailFileSuffix();
			File storeFile = new File(fileNameWidthExtension);
			for (int i = 0; storeFile.exists(); i++) {
				emlName = oriFileName + i;
				fileNameWidthExtension = this.receiverInfo.getEmailDir() + emlName
						+ this.receiverInfo.getEmailFileSuffix();
				storeFile = new File(fileNameWidthExtension);
			}
			this.currentEmailFileName = emlName;
			System.out.println("邮件消息的存储路径: " + fileNameWidthExtension);
			// 将邮件消息的内容写入ByteArrayOutputStream流中
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			message.writeTo(baos);
			// 读取邮件消息流中的数据
			StringReader in = new StringReader(baos.toString());
			// 存储到文件
			saveFile(fileNameWidthExtension, in);
		} catch (MessagingException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/*
	 * 解析邮件
	 */
	private void parseMessage(Message message) throws IOException, MessagingException {
		Object content = message.getContent();
		// 处理多部分邮件
		if (content instanceof Multipart) {
			handleMultipart((Multipart) content);
		} else {
			handlePart(message);
		}
	}

	/*
	 * 解析Multipart
	 */
	private void handleMultipart(Multipart multipart) throws MessagingException, IOException {
		for (int i = 0, n = multipart.getCount(); i < n; i++) {
			handlePart(multipart.getBodyPart(i));
		}
	}

	/*
	 * 解析指定part,从中提取文件
	 */
	private void handlePart(Part part) throws MessagingException, IOException {
		String disposition = part.getDisposition();
		String contentType = part.getContentType();
		String fileNameWidthExtension = "";
		// 获得邮件的内容输入流
		InputStreamReader sbis = new InputStreamReader(part.getInputStream());
		// 没有附件的情况
		if (disposition == null) {
			if ((contentType.length() >= 10) && (contentType.toLowerCase().substring(0, 10).equals("text/plain"))) {
				fileNameWidthExtension = this.receiverInfo.getAttachmentDir() + this.currentEmailFileName + ".txt";
			} else if ((contentType.length() >= 9) // Check if html
					&& (contentType.toLowerCase().substring(0, 9).equals("text/html"))) {
				fileNameWidthExtension = this.receiverInfo.getAttachmentDir() + this.currentEmailFileName + ".html";
			} else if ((contentType.length() >= 9) // Check if html
					&& (contentType.toLowerCase().substring(0, 9).equals("image/gif"))) {
				fileNameWidthExtension = this.receiverInfo.getAttachmentDir() + this.currentEmailFileName + ".gif";
			} else if ((contentType.length() >= 11) && contentType.toLowerCase().substring(0, 11).equals("multipart/*")) {
				// System.out.println("multipart body: " + contentType);
				handleMultipart((Multipart) part.getContent());
			} else { // Unknown type
			// System.out.println("Other body: " + contentType);
				fileNameWidthExtension = this.receiverInfo.getAttachmentDir() + this.currentEmailFileName + ".txt";
			}
			// 存储内容文件
			System.out.println("保存邮件内容到：" + fileNameWidthExtension);
			saveFile(fileNameWidthExtension, sbis);

			return;
		}

		// 各种有附件的情况
		String name = "";
		if (disposition.equalsIgnoreCase(Part.ATTACHMENT)) {
			name = getFileName(part);
			// System.out.println("Attachment: " + name + " : "
			// + contentType);
			fileNameWidthExtension = this.receiverInfo.getAttachmentDir() + name;
		} else if (disposition.equalsIgnoreCase(Part.INLINE)) {
			name = getFileName(part);
			// System.out.println("Inline: " + name + " : "
			// + contentType);
			fileNameWidthExtension = this.receiverInfo.getAttachmentDir() + name;
		} else {
			// System.out.println("Other: " + disposition);
		}
		// 存储各类附件
		if (!fileNameWidthExtension.equals("")) {
			System.out.println("保存邮件附件到：" + fileNameWidthExtension);
			saveFile(fileNameWidthExtension, sbis);
		}
	}

	private String getFileName(Part part) throws MessagingException, UnsupportedEncodingException {
		String fileName = part.getFileName();
		fileName = MimeUtility.decodeText(fileName);
		String name = fileName;
		if (fileName != null) {
			int index = fileName.lastIndexOf("/");
			if (index != -1) {
				name = fileName.substring(index + 1);
			}
		}
		return name;
	}

	/**
	 * 保存文件内容
	 * 
	 * @param fileName
	 *            文件名
	 * @param input
	 *            输入流
	 * @throws IOException
	 */
	private void saveFile(String fileName, Reader input) throws IOException {

		// 为了放置文件名重名，在重名的文件名后面天上数字
		File file = new File(fileName);
		// 先取得文件名的后缀
		int lastDot = fileName.lastIndexOf(".");
		String extension = fileName.substring(lastDot);
		fileName = fileName.substring(0, lastDot);
		for (int i = 0; file.exists(); i++) {
			// 　如果文件重名，则添加i
			file = new File(fileName + i + extension);
		}
		// 从输入流中读取数据，写入文件输出流
		FileWriter fos = new FileWriter(file);
		BufferedWriter bos = new BufferedWriter(fos);
		BufferedReader bis = new BufferedReader(input);
		int aByte;
		while ((aByte = bis.read()) != -1) {
			bos.write(aByte);
		}
		// 关闭流
		bos.flush();
		bos.close();
		bis.close();
	}

	/**
	 * 获得尖括号之间的字符
	 * 
	 * @param str
	 * @return
	 * @throws Exception
	 */
	private String getInfoBetweenBrackets(String str) throws Exception {
		int i, j; // 用于标识字符串中的"<"和">"的位置
		if (str == null) {
			str = "error";
			return str;
		}
		i = str.lastIndexOf("<");
		j = str.lastIndexOf(">");
		if (i != -1 && j != -1) {
			str = str.substring(i + 1, j);
		}
		return str;
	}

	public static void main(String[] args) throws Exception {
		MailReceiverInfo receiverInfo = new MailReceiverInfo();
		receiverInfo.setMailServerHost("123.125.50.29");
		receiverInfo.setMailServerPort("110");
		receiverInfo.setValidate(true);
		receiverInfo.setUserName("water_season");
		receiverInfo.setPassword("52609760");
		receiverInfo.setAttachmentDir(Attribute.getSdCard()+"/imail/");
		receiverInfo.setEmailDir(Attribute.getSdCard()+"/imail/");

		MailReceiver receiver = new MailReceiver(receiverInfo);
		receiver.receiveAllMail();
		Message[] ms = receiver.getMessages();
	}

}
